home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource2
/
sclib_2
/
2_6
/
v6n6060a.txt
< prev
next >
Wrap
Text File
|
1995-11-01
|
15KB
|
504 lines
/* *********************************************************************
*
*
* A Simple Model for Hiding Surfaces
* Jay Martin Anderson
* Franklin & Marshall College, Lancaster, Pennsylvania
* Tymlabs Corporation, Austin, Texas
*
* This program draws a three-dimensional object described by a table
* of faces using hidden-surface elimination.
* The algorithm is described briefly in M. Berger, "Computer Graphics
* with Pascal," (Benjamin/Cummings, 1986), and in somewhat greater
* detail by R. J. Rost, reported in Creative Computing, February, 1984
.
*
* Implemented for HP CRTs on HP 3000, using Pascal and AGL (HP's graph
ics
* language, April, 1984; revised, April, 1986; March, 1988;
* implemented in Tymlabs' C/3000 for the HP 3000, May 1988.
*
* Implemented May, 1988, for the Apple Macintosh (monochrome), using
* Lightspeed C and Quickdraw graphics (Macintosh toolbox).
*
* ********************************************************************
*/
/* ********************* #INCLUDEd files *****************************
*/
#include "stdio.h"
/* Use the "stdio" window provided by Lightspeed C, which is
* 500 pixels wide by 288 pixels tall, rather than using window
* management from Mac toolbox directly.
*/
#include "unix.h"
#include "Quickdraw.h"
#include "math.h"
/* ********************* Some Common DEFINEs *************************
*/
#define MAXFACES 60 /* maximum number of faces */
#define MAXPTS 100 /* maximum number of vertices */
#define NVF 4 /* vertices/face; squares */
#define HUGE_VAL 1.0E77 /* very large number */
#define TRUE 1
#define FALSE 0
#define sysPatListID 0 /* Resource ID, patterns */
typedef float MATRIX[4][4]; /* 4x4 matrix of real numers */
struct faceS
{
float z; /* smallest Z, eye coords, for face */
int color; /* "color" of this face; a Mac gray pattern */
int v[NVF]; /* list of vertices for this face */
};
/* ********************* Global Variables ****************************
*/
int v[MAXFACES][NVF]; /* vertex list */
float x0[MAXPTS], y0[MAXPTS], z0[MAXPTS]; /* original coordinates */
float xt[MAXPTS], yt[MAXPTS], zt[MAXPTS]; /* transformed coordinates */
float xs[MAXPTS], ys[MAXPTS]; /* screen coordinates */
struct faceS faceList[MAXFACES]; /* list of faces */
MATRIX view; /* viewing transformation */
float eyex, eyey, eyez; /* position of eye or viewer */
float fx, fy, fz; /* where eye is focussing */
float ds; /* scale factor */
float horizRotn; /* rotation of horizon */
float n1, n2, n3; /* normal to a face */
int numFaces, numVertices, numVisFaces;
FILE *theFile; /* points to data file */
int readFaceData()
{
/* Hardcoded to read from a file 9 cubes: 72 vertices and
* 54 faces. The name of the file is "cubedata," and it is
* presumed to be in the same folder as this project. Standard
* C-library I/O is used, rather than Macintosh Toolbox I/O.
* File is opened in "initStuff" and closed in "finishStuff".
*/
int i;
for (i = 0; i < numVertices; i++)
fscanf(theFile, "%f %f %f",&x0[i],&y0[i],&z0[i]);
for (i = 0; i < numFaces; i++)
fscanf(theFile, "%d %d %d %d",&v[i][0],&v[i][1],&v[i][2],&v[i][3]);
fseek(theFile, 0L, 0); /* rewind the file */
if (ferror(theFile))
{
puts("error reading CUBEDATA file");
return(FALSE);
}
else
{
return(TRUE);
}
}
int getParams()
{
/* Hardcoded for one reasonable view of the scene of 9 cubes. */
/* Position of eye */
eyex = 20.0; eyey = 25.0; eyez = 25.0;
/* Focus point; where eye is looking */
fx = fy = fz = 0.0;
return(TRUE);
}
void initMat(M)
MATRIX M;
{
/* Construct a 4x4 idenity matrix */
int i, j; /* subscript; loop index */
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
M[i][j] = (i != j) ? 0.0 : 1.0;
}
void multMat(M3, M1, M2)
MATRIX M1, M2, M3;
{
/* Multiply M1 x M2 and put result in M3; 4x4 square matrices */
int i, j, k;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
{
M3[i][j] = 0.0;
for (k = 0; k < 4; k++) M3[i][j] += M1[i][k]*M2[k][j];
}
}
void calcViewMatrix()
{
/* Calculates the viewing transformation matrix */
int i, j; /* subscript; loop index */
MATRIX t1, t2; /* temp matrices for transformation */
double d1, d2; /* temp results */
initMat(view);
/* translate origin to eye position */
view[3][0] = -eyex;
view[3][1] = -eyey;
view[3][2] = -eyez;
initMat(t1);
/* rotate about x-axis by 90 degrees */
t1[1][1] = 0.0;
t1[2][2] = 0.0;
t1[1][2] = -1.0;
t1[2][1] = 1.0;
multMat(t2, view, t1);
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) view[i][j] = t2[i][j];
initMat(t1);
/* rotate about y-axis by an angle dependent on focus point */
fx = eyex - fx;
fy = eyey - fy;
fz = eyez - fz;
d1 = sqrt((double)(fx*fx + fy*fy));
if (fabs(d1) > 0.0001)
{
t1[0][0] = -fy/d1;
t1[2][2] = -fy/d1;
t1[0][2] = fx/d1;
t1[2][0] = -fx/d1;
multMat(t2, view, t1);
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) view[i][j] = t2[i][j];
}
initMat(t1);
/* rotate about x-axis by angle dependent on focus point */
d2 = sqrt((double)(fx*fx + fy*fy + fz*fz));
if (fabs(d2) > 0.0001)
{
t1[1][1] = d1/d2;
t1[2][2] = d1/d2;
t1[1][2] = fz/d2;
t1[2][1] = -fz/d2;
multMat(t2, view, t1);
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) view[i][j] = t2[i][j];
}
initMat(t1);
/* rotate about z-axis to rotate horizon */
horizRotn *= PI/180.0; /* convert degrees to radians */
t1[0][0] = t1[1][1] = (float)cos((double)horizRotn);
t1[0][1] = (float)sin((double)horizRotn);
t1[1][0] = -t1[0][1];
multMat(t2, view, t1);
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) view[i][j] = t2[i][j];
initMat(t1);
/* invert the z-axis */
t1[2][2] = -1.0;
/* and scale according to D/S ratio */
t1[0][0] = ds;
t1[1][1] = ds;
multMat(t2, view, t1);
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++) view[i][j] = t2[i][j];
}
void initStuff()
{
/* Whatever needs to be initialized; may be device-dependent;
* may be implementation-dependent. Includes fixed parameters
* for this example.
*/
numVertices = 72;
numFaces = 54;
/* Write at upper left of console window */
gotoxy(0,0);
puts("Example 2. Cube of cubes.");
/* Open the data file */
theFile = fopen("cubedata","r");
if (theFile == NULL)
{
puts("error opening CUBEDATA file");
}
/* Horizon rotation */
horizRotn = 0.0;
/* Scaling factor */
ds = 1.0;
}
void finishStuff()
{
/* Whatever needs to be closed, etc. May be device-dependent;
* may be implementation-dependent.
* In this example, close the data file.
*/
fclose(theFile);
}
void transform(px, py, pz)
float *px, *py, *pz;
{
/* Transforms a point, pointed to be px, py, pz, by the view transf. */
float temp1, temp2, temp3;
/* Transforms a point, pointed to by px, py, pz, by view transform */
temp1 = *px, temp2 = *py, temp3 = *pz;
*px = view[0][0]*temp1+view[1][0]*temp2+view[2][0]*temp3+view[3][0];
*py = view[0][1]*temp1+view[1][1]*temp2+view[2][1]*temp3+view[3][1];
*pz = view[0][2]*temp1+view[1][2]*temp2+view[2][2]*temp3+view[3][2];
}
void transformAll()
{
/* Transforms all points */
int i;
for (i = 0; i < numVertices; i++) transform(&xt[i],&yt[i],&zt[i]);
}
void getCrossProduct(nv1,nv2,nv3)
int nv1,nv2,nv3;
{
/* Computes cross-product of vectors representing two sides of a face;
* the two sides are the vectors from nv2-nv1 and nv3-nv1. The
* cross-product is the outward normal to this face; its three
* components are stored in the global variables n1, n2, n3.
*/
float v1, v2, v3, w1, w2, w3;
v1 = xt[nv2] - xt[nv1];
v2 = yt[nv2] - yt[nv1];
v3 = zt[nv2] - zt[nv1];
w1 = xt[nv3] - xt[nv1];
w2 = yt[nv3] - yt[nv1];
w3 = zt[nv3] - zt[nv1];
n1 = v2*w3 - v3*w2;
n2 = v3*w1 - v1*w3;
n3 = v1*w2 - v2*w1;
}
void getDotProduct(pdot, nv1)
float *pdot; /* the dot product */
int nv1; /* which vertex */
{
/* Computes the dot product of the outbound normal from a face,
* kept in the global variables n1, n2, n3, with a vector from
* the eye to the face, using a vector from the eye to vertex nv1.
*/
*pdot = n1*xt[nv1] + n2*yt[nv1] + n3*zt[nv1];
}
void eyeToScreen(x, y, z, px, py)
float x, y, z, *px, *py;
{
/* Transforms a point from x, y, z eye coordinates to
* x, y screen coordinates. This includes the projection
* and the use of the physical screen size in pixels
*/
float xC, yC; /* center of screen */
float xR, yR; /* width of screen */
/* hardcoded for Lightspeed "stdio window" for now */
xC = 250, yC = 144;
xR = 500, yR = 288;
*px = xR*(x/z) + xC;
*py = yR - (yR*(y/z) + yC);
}
int zcompare(pFace1, pFace2)
struct faceS *pFace1, *pFace2;
{
/* Comparison function used by C library function "qsort" to do
* sorting. This function compares the minimum z coordinate of
* the faces, so that faces are sorted in the order of distance
* from the eye. This is a DESCENDING sort!!
*/
if (pFace1->z < pFace2->z) return(1);
else if (pFace1->z > pFace2->z) return(-1);
else return(0);
}
void sortFaces()
{
/* Sorts the faceList in ascending order of Z for a Z-buffer
* display; uses quicksort as implemented in library.
* Requires the preceding function which compares z values for
* faces.
*/
qsort(faceList, numVisFaces, sizeof(struct faceS), zcompare);
}
/* ******************** QUICKDRAW GRAPHICS PROCEDURES *****************
*/
void drawFace(f)
int f;
{
/* This procedure draws a face. It is device- and implementation-
* dependent. In this implementation, it uses the Lightspeed C
* "stdio" or console window, and Macintosh Quickdraw procedures.
*
* Each face is a polygon, and is developed with the sequence
* OpenPoly...MoveTo or LineTo...ClosePoly. The "color" of a
* face is interpreted as a QuickDraw pen pattern, and is looked
* up in the system pattern list. The system pattern list is
* presumed to be available. It consists of 38 patterns. The
* first six of these patterns are used for the six faces of
* the cubes. Each face is then "framed," that is, outlined
* in solid black.
*
* In this example, a transparent face is marked with color 0.
* Transparent faces are not filled with pattern.
*/
PolyHandle aFace;
Pattern thePattern;
aFace = OpenPoly();
MoveTo((int)xs[faceList[f].v[0]],(int)ys[faceList[f].v[0]]);
LineTo((int)xs[faceList[f].v[1]],(int)ys[faceList[f].v[1]]);
LineTo((int)xs[faceList[f].v[2]],(int)ys[faceList[f].v[2]]);
LineTo((int)xs[faceList[f].v[3]],(int)ys[faceList[f].v[3]]);
LineTo((int)xs[faceList[f].v[0]],(int)ys[faceList[f].v[0]]);
ClosePoly();
if (faceList[f].color > 0)
{
GetIndPattern(thePattern, sysPatListID, faceList[f].color);
PenPat(thePattern);
PaintPoly(aFace);
}
PenPat(black);
FramePoly(aFace);
KillPoly(aFace);
}
void screenAll()
{
/* Projects from eye coordinates to screen coordinates */
int i;
for (i = 0; i < numVertices; i++)
{
eyeToScreen(xt[i], yt[i], zt[i], &xs[i], &ys[i]);
}
}
void doFace(f, ff, opaque)
int f, /* index into vertex list */
ff, /* index into faceList */
opaque; /* TRUE/FALSE: is face opaque? */
{
/* Adds a face to the visible face list and computes its z */
float zmin;
int i;
zmin = HUGE_VAL;
for (i = 0; i < NVF; i++)
{
faceList[ff].v[i] = v[f][i];
if (zt[v[f][i]] < zmin) zmin = zt[v[f][i]];
}
faceList[ff].z = zmin;
/* If the face is opaque, fill it with one of the first six
* Macintosh patterns. Otherwise, set its color to 0 for
* transparency.
*/
if (opaque) faceList[ff].color = (f%6) + 1;
else faceList[ff].color = 0;
}
void copyData()
{
/* Copies the original data so we can transform it without
* destroying the original
*/
int i;
for (i = 0; i < numVertices; i++)
xt[i] = x0[i], yt[i] = y0[i], zt[i] = z0[i];
}
void drawPicture()
{
/* This prcoedure draws the scene, with hidden surfaces removed */
int i; /* subscript; loop index */
float dot; /* the dot product */
transformAll();
screenAll();
/* First, do the opaque faces (8 cubes on the 8 corners) */
numVisFaces = 0;
for (i = 0; i < numFaces - 6; i++)
{
/* ***** HERE IS THE HIDDEN SURFACE REMOVAL ALGORITHM!! *****
* Compute the cross-product of any two sides of a face (we use
* the sides 1-0 and 2-0). This gets an outbound normal from
* the face. Then take the dot product of the outbound normal
* with a vector from the eye to the face (we use the vector from
* the eye to vertex 0). If the dot product is non-negative,
* the face is visible.
* ********************************************************** */
getCrossProduct(v[i][0], v[i][1], v[i][2]);
getDotProduct(&dot, i);
if (dot >= 0.0)
{
doFace(i, numVisFaces, TRUE);
numVisFaces++;
}
}
/* Now do the transparent faces (1 large cube); all faces are
* visible and uncolored!
*/
for (i = numFaces - 6; i < numFaces; i++)
{
doFace(i, numVisFaces, FALSE);
numVisFaces++;
}
/* Arrange the faces in decreasing order of distance-from-eye, so
* that the last face drawn is the one nearest the viewer. This
* extends the basic algorithm to allow sences to be made up
* of more than one solid figure, in some cases. It is called a
* z-buffer algorithm, because the faces to be drawn are buffered
* in order of their "z" value. Notice that the z-buffer algorithm
* fails (partly) in this case, because the faces of the one
* large transparent cube penetrate the faces of the eight small
* opaque cubes. The algorithm cannot accomodate penetrating
* surfaces.
*/
sortFaces();
for (i = 0; i < numFaces; i++) drawFace(i);
}
main()
{
int ok;
int i,j;
initStuff();
ok = readFaceData();
copyData();
if (ok)
{
ok = getParams();
if (ok)
{
calcViewMatrix();
drawPicture();
}
}
finishStuff();
}